<?php
class Mozg_Source_Db extends PDO
{
	protected $_tablePrefix = '';
	protected $_tablesList = array();
	protected $_tablesPlaceList = array();
	protected $_tableConfig = array();

	static protected $_queries = array();

	public function __construct()
	{
		$args = func_get_args();
		if (version_compare( phpversion(), '5.3', '<' ))
		{
			call_user_func_array(array($this, 'parent::__construct'), $args);
		}
		else
		{
			call_user_func_array('parent::__construct', $args);
		}

		$tables = (array)$this->getDBTablesCached();
		$this->_tablesList = array_values($tables);
		$this->_tablesPlaceList = array_keys($tables);
	}

	protected function getDBTablesCached()
	{
		list(,$sourceName) = explode('_', get_class($this), 2);
		$cacheDbFile = _CACHE_PATH. '_db/' .strtolower($sourceName). '_tablesList';

		if (Mozg_Storage::Exists($cacheDbFile))
		{
			$tables = Mozg_Storage::Read($cacheDbFile);
			if (isset($tables[$this->_tablePrefix])) return $tables[$this->_tablePrefix];
		}

		$tables = $this->getDBTables();

		try {
			Mozg_Storage::Save($cacheDbFile, $tables);
		}
		catch (Mozg_Exception $e)
		{

		}

		return $tables[$this->_tablePrefix];
	}

	protected function getDBTables()
	{
		$stmt = $this->query('show tables');
		if (!$stmt) return array();

		$stmt->setFetchMode(PDO::FETCH_COLUMN, 0);
		$tables = $stmt->fetchAll();

		$tablePrefix = $this->_tablePrefix. '_';
		$prefLength = strlen($tablePrefix);

		$myTables = array();
		foreach ($tables as $table)
		{
			if ($prefLength && strpos($table, $tablePrefix) !== 0) continue;
			$nTable = substr($table, $prefLength);
			$myTables['%%' .$nTable. '%%'] = $table;
		}

		return array($this->_tablePrefix => $myTables);
	}

	public function prepare($sql, $driverOptions = array())
	{
		$result = parent::prepare($this->replaceTableMarkers($sql), $driverOptions);
		if (false === $result) $this->handleError();

		return $result;
	}

	public function query( )
	{
		$args = func_get_args();
		$args[0] = $this->replaceTableMarkers($args[0]);//sql

		//echo $args[0];

		if (version_compare( phpversion(), '5.3', '<' ))
		{
			$result = call_user_func_array(array($this, 'parent::query'), $args);
		}
		else
		{
			$result = call_user_func_array('parent::query', $args);
		}

		if (false === $result) $this->handleError();

		return $result;
	}

	public function PrepareStmt($query)
	{
		$stmt = $this->query($query);
	    $stmt->setFetchMode(PDO::FETCH_CLASS, 'Mozg_Source_Table_Data');
	    return $stmt;
	}

	public function fetchObject($query, $entityPath)
	{
		$entityConfig = Mozg_Property_Config::Get($entityPath);

        $row = $this->PrepareStmt($query)->fetch();
        return Mozg_Entity::Get($entityConfig, $row->ExportData());
	}

	public function fetchObjectAll($query, $entityPath)
	{
		$stmt = $this->PrepareStmt($query);

		$entityConfig = Mozg_Property_Config::Get($entityPath);

		$isPrimaryKey = (bool)$entityConfig->GetPrimaryKey();

	    $data = array();
	    foreach ($stmt as $row)
        {
        	$entity = Mozg_Entity::Get($entityConfig, $row->ExportData());
        	if ($isPrimaryKey)
        	{
				$data[$entity->PrimaryKeyValue()] = $entity;
        	}
        	else
        	{
				$data[] = $entity;
        	}
        }

	    return $data;
	}

	public function exec($sql)
	{
		$result = parent::exec($this->replaceTableMarkers($sql));
		if (false === $result) $this->handleError();

		return $result;
	}

	protected function replaceTableMarkers($query)
	{
		$query = str_replace($this->_tablesPlaceList, $this->_tablesList, $query);
		self::$_queries[] = $query;
		return $query;
	}

	protected function handleError()
	{
		$error = $this->errorInfo();
		if (!is_null($error[1])) throw new Mozg_Exception_System("MySQL error: code = " .$error[1]. "; message = " .$error[2]);
	}

	static public function GetExecutedQueries()
	{
		return self::$_queries;
	}


	protected $_tableFields = array();

	protected function GetTableFields($tableName)
	{
		if (isset($this->_tableFields[$tableName])) return $this->_tableFields[$tableName];

		list(,$sourceName) = explode('_', get_class($this), 2);
		$tableCacheFile = _CACHE_PATH. '_db/' .strtolower($sourceName). '_' .$tableName. '_fields';

		if (Mozg_Storage::Exists($tableCacheFile))
		{
			$this->_tableFields[$tableName] = Mozg_Storage::Read($tableCacheFile);
		}
		else
		{
			$stmt = $this->query('DESCRIBE ' .$this->_tablePrefix. '_' .$tableName);
			$stmt->setFetchMode(PDO::FETCH_ASSOC);

			foreach ($stmt as $row)
			{
				$this->_tableFields[$tableName][$row['Field']] = array_merge(array(
					'default' => !is_null($row['Default']),
					'null' => strtolower($row['Null']) == 'yes' ? true : false
				), $this->ParseFieldType($row['Type']));
			}

			try {
				Mozg_Storage::Save($tableCacheFile, $this->_tableFields[$tableName]);
			}
			catch (Mozg_Exception $e)
			{

			}
		}

		return $this->_tableFields[$tableName];
	}

	protected function ParseFieldType($type)
	{
		list($type) = explode(' ', $type, 2);
		list($type, $length) = explode('(', $type, 2);
		$length = rtrim($length, ')');

		return array('type' => $type, 'length' => $length);
	}

    public function LockTable(array $tableList)
    {
    	if (!is_array($tableList)) $tableList = array($tableList);
    	$sqlTableList = array();
    	foreach ($tableList as $table)
    	{
			$sqlTableList[] = $table. ' WRITE';
    	}
    	$this->exec("LOCK TABLES " .implode(',', $sqlTableList));
    }

    public function UnLockTable()
    {
    	$this->exec("UNLOCK TABLES");
    }

	public function Table($configPath)
	{
		if (!isset($this->_tableConfig[$configPath]) || !($this->_tableConfig[$configPath] instanceof Mozg_Source_Table_Fields))
		{
			$config = Mozg_Source_Table_Config::Get($configPath);
			$fields = $this->GetTableFields($config->GetTable());

			$this->_tableConfig[$configPath] = new Mozg_Source_Table_Fields($fields, $config->GetFieldsMap(), $this);
		}

		return $this->_tableConfig[$configPath];
	}

	public function __destruct()
	{
		//print_r(self::GetExecutedQueries());
	}
}

